#include "tsdna.h"
#include "tscell.h"
#include "tssynapse.h"
#include "sqlite3.h"
#include <stdlib.h>
#include <glib-object.h>

typedef struct _TsDna {
  GObject parent_instance;

  gint64 id;
  gint64 threshold;

  gboolean status;

  gchar *name;

  GList *dendrite;//树突 input 1,2,3
  GList *axon;//轴突 output 5,6

  gpointer *cell;//all cell list
  gpointer son;
  GObject *next;
} TsDna;
enum {
  PROP_0,
  PROP_ID, PROP_THRESHOLD,
  PROP_STATUS,
  PROP_NAME,
  PROP_DENDRITE, PROP_AXON, PROP_CELL,
  PROP_SON, PROP_NEXT,
  N_PROPERTIES };

static GParamSpec *properties[N_PROPERTIES] = { NULL, };

G_DEFINE_TYPE (TsDna, ts_dna, G_TYPE_OBJECT)

static void
ts_dna_set_id (TsDna *obj,
                     gint64        value)
{
  if (obj->id != value)
    {
      obj->id = value;

      g_assert (properties[PROP_ID] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_ID]);
    }
}
static void
ts_dna_set_threshold (TsDna *obj,
                     gint64        value)
{
  if (obj->threshold != value)
    {
      obj->threshold = value;

      g_assert (properties[PROP_THRESHOLD] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_THRESHOLD]);
    }
}
static void
ts_dna_set_status (TsDna *obj,
                     gboolean    status)
{
  status = !!status;

  if (obj->status != status)
    {
      obj->status = status;

      g_assert (properties[PROP_STATUS] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_STATUS]);
    }
}

static void
ts_dna_set_name (TsDna  *obj,
                     const gchar *name)
{
  if (g_strcmp0 (obj->name, name) != 0)
    {
      g_free (obj->name);
      obj->name = g_strdup (name);

      g_assert (properties[PROP_NAME] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_NAME]);
    }
}

static void
ts_dna_set_dendrite (TsDna  *obj,
                     gpointer value)
{
  obj->dendrite = value;
}
static void
ts_dna_set_axon (TsDna  *obj,
                     gpointer value)
{
  obj->axon = value;
}
static void
ts_dna_set_cell (TsDna  *obj,
                     gpointer value)
{
  obj->cell = value;
}
static void
ts_dna_set_son (TsDna  *obj,
                     gpointer value)
{
  obj->son = value;
}
static void
ts_dna_set_next (TsDna  *obj,
                     gpointer value)
{
  obj->next = (GObject *) value;
}

static void
ts_dna_finalize (GObject *gobject)
{
  TsDna *self = (TsDna *) gobject;

  g_free (self->name);

  g_object_notify (gobject, "port");
  g_object_notify_by_pspec (gobject, properties[PROP_STATUS]);

  G_OBJECT_CLASS (ts_dna_parent_class)->finalize (gobject);
}

static void
ts_dna_set_property (GObject *gobject,
                          guint prop_id,
                          const GValue *value,
                          GParamSpec *pspec)
{
  TsDna *tobj = (TsDna *) gobject;

  g_assert_cmpint (prop_id, !=, 0);
  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
  g_assert (pspec == properties[prop_id]);

  switch (prop_id)
    {
    case PROP_ID:
      ts_dna_set_id (tobj, g_value_get_int64 (value));
      break;
    case PROP_THRESHOLD:
      ts_dna_set_threshold (tobj, g_value_get_int64 (value));
      break;

    case PROP_STATUS:
      ts_dna_set_status (tobj, g_value_get_boolean (value));
      break;
    case PROP_NAME:
      ts_dna_set_name (tobj, g_value_get_string (value));
      break;
    case PROP_DENDRITE:
      ts_dna_set_dendrite (tobj, g_value_get_pointer (value));
      break;
    case PROP_AXON:
      ts_dna_set_axon (tobj, g_value_get_pointer (value));
      break;
    case PROP_CELL:
      ts_dna_set_cell (tobj, g_value_get_pointer (value));
      break;

    case PROP_SON:
      ts_dna_set_son (tobj, g_value_get_pointer (value));
      break;
    case PROP_NEXT:
      ts_dna_set_next (tobj, g_value_get_object (value));
      break;

    default:
      g_assert_not_reached ();
    }
}

static void
ts_dna_get_property (GObject *gobject,
                          guint prop_id,
                          GValue *value,
                          GParamSpec *pspec)
{
  TsDna *tobj = (TsDna *) gobject;

  g_assert_cmpint (prop_id, !=, 0);
  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
  g_assert (pspec == properties[prop_id]);

  switch (prop_id)
    {
    case PROP_ID:
      g_value_set_int64 (value, tobj->id);
      break;
    case PROP_THRESHOLD:
      g_value_set_int64 (value, tobj->threshold);
      break;

    case PROP_STATUS:
      g_value_set_boolean (value, tobj->status);
      break;

    case PROP_NAME:
      g_value_set_string (value, tobj->name);
      break;

    case PROP_DENDRITE:
      g_value_set_pointer (value, tobj->dendrite);
      break;
    case PROP_AXON:
      g_value_set_pointer (value, tobj->axon);
      break;
    case PROP_CELL:
      g_value_set_pointer (value, tobj->cell);
      break;

    case PROP_NEXT:
      g_value_set_object (value, (gpointer) tobj->next);
      break;
    case PROP_SON:
      g_value_set_pointer (value, tobj->son);
      break;

    default:
      g_assert_not_reached ();
    }
}

static void
ts_dna_class_init (TsDnaClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  properties[PROP_ID] = g_param_spec_int64 ("id", "id", "id",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_THRESHOLD] = g_param_spec_int64 (
      "threshold", "threshold", "threshold",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);

  properties[PROP_STATUS] = g_param_spec_int ("status", "Status", "Status",
      -1, G_MAXINT, 0, G_PARAM_READWRITE);
  properties[PROP_NAME] = g_param_spec_string ("name", "Name", "Name",
      NULL, G_PARAM_READWRITE);

  properties[PROP_DENDRITE] = g_param_spec_pointer (
      "dendrite", "dendrite", "dendrite",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_AXON] = g_param_spec_pointer ("axon", "axon", "axon",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_CELL] = g_param_spec_pointer ("cell", "cell", "cell",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_SON] = g_param_spec_pointer ("son", "son", "son",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_NEXT] = g_param_spec_object ("next", "next", "next",
      G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);

  gobject_class->set_property = ts_dna_set_property;
  gobject_class->get_property = ts_dna_get_property;
  gobject_class->finalize = ts_dna_finalize;

  g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
}

static void
ts_dna_init (TsDna *self)
{
  //self->name = g_strdup ("zero");
 // TsDna *dna = NULL;
  //GList *list = NULL, *all = NULL;

}

TsDna *ts_dnas_get_object (TsDna *gobject,
                          const gchar *name,
                          gpointer value)
{
  TsDna *tobj = gobject;
  guint prop_id;
  gboolean found = FALSE;
  if (!g_strcmp0 (name, "id")) {
    prop_id = PROP_ID;
  }
  while (tobj) {
  //g_debug ("%s\tid:%d\n", __func__, tobj->id);
    switch (prop_id)
      {
      case PROP_ID:
        if (tobj->id == *(gint64 *)value) {
          found = TRUE;
        }
        break;
      default:
        g_assert_not_reached ();
      }
    if (found)
      break;
    tobj = (TsDna *) tobj->next;
  }
  return tobj;
}
GList *ts_dnas_get_list (TsDna *gobject,
                          const gchar *name,
                          gpointer value)
{
  TsDna *tobj = gobject;
  GList *l = NULL, *res = NULL;
  guint prop_id;
  gboolean found = FALSE;
  if (!g_strcmp0 (name, "id")) {
    prop_id = PROP_ID;
  } else if (!g_strcmp0 (name, "dendrite")) {
    prop_id = PROP_DENDRITE;
  } else if (!g_strcmp0 (name, "axon")) {
    prop_id = PROP_AXON;
  }
  while (tobj) {
  //g_debug ("%s\tid:%d\n", __func__, tobj->id);
    switch (prop_id)
      {
      case PROP_ID:
        if (tobj->id == *(gint64 *)value) {
          found = TRUE;
        }
        break;
      case PROP_DENDRITE:
        l = tobj->dendrite;
        while (l) {
          if (GPOINTER_TO_INT(l->data) == *(gint64 *)value) {
            res = g_list_append (res, tobj);
          }
          l = l->next;
        }
        break;
      case PROP_AXON:
        l = tobj->axon;
        while (l) {
          if (GPOINTER_TO_INT(l->data) == *(gint64 *)value) {
            res = g_list_append (res, l->data);
          }
          l = l->next;
        }
        break;
      default:
        g_assert_not_reached ();
      }
    if (found)
      break;
    tobj = (TsDna *) tobj->next;
  }
  return res;
}
TsDna *ts_dna_new (void)
{
  return g_object_new (TS_DNA_TYPE, NULL);
}
int tmp_sql_callback(void *ptr, int n_column, char **column_value, char **column_name)
{
  int rc = 0, i = 0;
  TsDna **dna = NULL, *now = NULL, *p = NULL, *next = NULL;

  dna = ptr;
  if (n_column == 0) {
    g_debug("sql: column is 0\n");
    rc = 1;
    goto err;
  }
  if (ptr) {
    now = ts_dna_new ();
    if (*dna) {
      p = *dna;
      while (p) {
        g_object_get (p, "next", &next, NULL);
        if (next) {
          p = next;
        } else {
          g_object_set (p, "next", now, NULL);
          break;
        }
      }
    } else {
      *dna = now;
    }
    for (i = 0; i < n_column; i++) {
      if (column_value[i]) {
        if (!g_strcmp0 (column_name[i], "id")) {
          g_object_set (now, column_name[i], atol (column_value[i]), NULL);
        } else if (!g_strcmp0 (column_name[i], "threshold")) {
          g_object_set (now, column_name[i], atoi (column_value[i]), NULL);
        } else if (!g_strcmp0 (column_name[i], "status")) {
          g_object_set (now, column_name[i], atoi (column_value[i]), NULL);
        } else {
          g_object_set (now, column_name[i], column_value[i], NULL);
        }
      }
    }
 //   g_debug ("new \t%s:%s\n", column_name[0], column_value[0]);
  }
// DEBUG("array:%s", json_object_get_string(obj));
err:
  return rc;
}
int synapse_sql_callback(void *ptr, int n_column, char **column_value, char **column_name)
{
  int rc = 0, proportion = 0, dendrite_id = 0;
  TsSynapse *ts;

  if (n_column == 0) {
    g_debug("sql: column is 0\n");
    rc = 1;
    goto err;
  }
  if (ptr) {
    //g_debug("%s: column is %d", column_value[0], n_column);
    if (column_value[2])
      proportion = atol (column_value[2]);
    if (column_value[3])
      dendrite_id = atol (column_value[3]);
    ts = ts_synapse_new (atol (column_value[0]), proportion, dendrite_id);
    if (n_column == 4) {//axon
      g_object_set (ts, "type", 0, NULL);
    } else {
      g_object_set (ts, "type", 1, NULL);
    }
    ts_synapses_add_end (ptr, ts);
 //   g_debug ("new \t%s:%s\n", column_name[0], column_value[0]);
  }
err:
  return rc;
}
TsDna *ts_dna_from_sql (const gchar *filename)
{
  TsDna *start = NULL, *dna = NULL, *next = NULL;
  sqlite3 *sql = NULL;
  gchar statement[1024] = {0}, *errmsg = NULL;
  gint rc;
  gint64 id;

  rc = sqlite3_open(filename, &sql);
  if (rc != SQLITE_OK) {
    g_warning(sqlite3_errmsg(sql));
    goto out;
  }
  g_snprintf (statement, sizeof(statement),
      "select * from dna;");
  rc = sqlite3_exec(sql, statement, tmp_sql_callback, &start, &errmsg);
  if (rc != SQLITE_OK) {
    g_warning("SELECT:%s:%s:%d\n", statement, errmsg, rc);
  }
  dna = start;
  while (dna) {
    g_object_get (dna, "next", &next, "id", &id, NULL);
    g_debug ("L%d f-%s dna:%ld", __LINE__, __func__, id);
    g_snprintf (statement, sizeof(statement),
        "select * from dendrite where parent_id = %ld;", id);
    rc = sqlite3_exec(sql, statement, synapse_sql_callback, &(dna->dendrite), &errmsg);
    if (rc != SQLITE_OK) {
      g_warning("SELECT:%s:%s:%d\n", statement, errmsg, rc);
    }
    dna = next;
  }
  dna = start;
  while (dna) {
    g_object_get (dna, "next", &next, "id", &id, NULL);
    g_debug ("L%d f-%s dna:%ld", __LINE__, __func__, id);
    g_snprintf (statement, sizeof(statement),
        "select * from axon where parent_id = %ld;", id);
    rc = sqlite3_exec(sql, statement, synapse_sql_callback, &(dna->axon), &errmsg);
    if (rc != SQLITE_OK) {
      g_warning("SELECT:%s:%s:%d\n", statement, errmsg, rc);
    }
    dna = next;
  }
out:
  return start;
}
void ts_dna_cell_set_axon_value (TsDna *dna, gint64 cell_id, gint64 axon_id,
    gint64 value, gpointer user_data)
{
  TsDna *now_dna;
 // TsCell *cell;
  TsSynapse *axon, *synapse;
  now_dna = ts_dnas_get_object (dna, "id", &cell_id);
  if (!now_dna) {
    g_warning ("no this dna:%"G_GINT64_FORMAT, cell_id);
    goto out;
  }
//  g_object_get (now_dna, "cell", &cell, NULL);
  g_object_get (now_dna, "axon", &axon, NULL);
  synapse = ts_synapses_get_object (axon, "id", &axon_id);
  if (!synapse) {
    g_warning ("no this axon synapse:%"G_GINT64_FORMAT, axon_id);
    goto out;
  }
  g_object_set (synapse, "value", value, NULL);
out:
  return;
}
